package router

import (
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"go.uber.org/zap"
	assets "intranetWs/ion-sfu"
	jrpc "intranetWs/ion-sfu/cmd/signal/json-rpc"
	"intranetWs/middleware"
	"intranetWs/static"
	"intranetWs/util"
	"intranetWs/ws"
	"net"
	"net/http"
	"net/http/httputil"
	"os"
	"runtime/debug"
	"strings"
	"time"
)

func SetupRouter(m string) *gin.Engine {
	gin.SetMode(m)
	r := gin.New()
	r.Use(gin.Logger(), gin.Recovery(), middleware.Cors())
	// r.Use(middleware.Authorize())
	r.Use(ReqIdMiddleware(), Ginzap(util.Logger, time.RFC3339, true), RecoveryWithZap(util.Logger, true))
	//r.GET("main/*demo", gin.WrapH(http.FileServer(http.FS(assets.Demo))))
	r.Static("file-temp", "./file-temp")
	// demo对应目录名
	r.GET("demo/*demo", gin.WrapH(http.FileServer(http.FS(assets.JsonRpc))))

	// 白板页面 wb是和文件夹名一样
	r.GET("wb/*wb", gin.WrapH(http.FileServer(http.FS(static.Wb))))
	// wps
	r.GET("wpsjs/*wpsjs", gin.WrapH(http.FileServer(http.FS(static.Wpsjs))))
	// 测试
	r.GET("test/*test", gin.WrapH(http.FileServer(http.FS(static.WbTest))))

	jrpc_r := r.Group("jrpc")
	{
		jrpc_r.GET("sessions", jrpc.Sessions)
		jrpc_r.GET("username", jrpc.UserName)
	}
	wsGroup := r.Group("ws")
	{
		wsGroup.GET("/:channel", ws.WebsocketManager.WsClient)
		// 远程开关
		wsGroup.POST("remote/:type", ws.Switch)
	}
	return r
}

func ReqIdMiddleware() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		reqId := ctx.Request.Header.Get("X-Request-Id")
		if reqId == "" {
			reqId = uuid.NewString()
			logger := util.Logger.With(zap.String("X-Request-Id", reqId))
			ctx.Request.Header.Set("X-Request-Id", reqId)
			ctx.Set("logger", logger)
		}
		// 写入响应
		ctx.Header("X-Request-Id", reqId)
		ctx.Next()
	}
}

// getRealIp 获取请求的ip
func getRealIp(c *gin.Context) string {
	if len(c.GetHeader("X-Forwarded-For")) == 0 {
		return c.ClientIP()
	}
	return c.GetHeader("X-Forwarded-For")
}

func Ginzap(logger *zap.Logger, timeFormat string, utc bool) gin.HandlerFunc {
	return func(c *gin.Context) {
		reqId := c.Request.Header.Get("X-Request-Id")
		start := time.Now()
		// some evil middlewares modify this values
		path := c.Request.URL.Path
		query := c.Request.URL.RawQuery
		c.Next()

		end := time.Now()
		latency := end.Sub(start)
		if utc {
			end = end.UTC()
		}

		if len(c.Errors) > 0 {
			// Append error field if this is an erroneous request.
			for _, e := range c.Errors.Errors() {
				logger.Error(e)
			}
		} else {
			logger.Info(path,
				zap.String("X-Request-Id", reqId),
				zap.Int("status", c.Writer.Status()),
				zap.String("method", c.Request.Method),
				zap.String("path", path),
				zap.String("query", query),
				zap.String("ip", getRealIp(c)),
				zap.String("user-agent", c.Request.UserAgent()),
				zap.String("time", end.Format(timeFormat)),
				zap.Duration("latency", latency),
			)
		}
	}
}

func RecoveryWithZap(logger *zap.Logger, stack bool) gin.HandlerFunc {
	return func(c *gin.Context) {
		defer func() {
			if err := recover(); err != nil {
				// Check for a broken connection, as it is not really a
				// condition that warrants a panic stack trace.
				var brokenPipe bool
				if ne, ok := err.(*net.OpError); ok {
					if se, ok := ne.Err.(*os.SyscallError); ok {
						if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
							brokenPipe = true
						}
					}
				}

				httpRequest, _ := httputil.DumpRequest(c.Request, false)
				if brokenPipe {
					logger.Error(c.Request.URL.Path,
						zap.Any("error", err),
						zap.String("request", string(httpRequest)),
					)
					// If the connection is dead, we can't write a status to it.
					c.Error(err.(error)) // nolint: errcheck
					c.Abort()
					return
				}

				if stack {
					logger.Error("[Recovery from panic]",
						zap.Time("time", time.Now()),
						zap.Any("error", err),
						zap.String("request", string(httpRequest)),
						zap.String("stack", string(debug.Stack())),
					)
				} else {
					logger.Error("[Recovery from panic]",
						zap.Time("time", time.Now()),
						zap.Any("error", err),
						zap.String("request", string(httpRequest)),
					)
				}
				c.AbortWithStatus(http.StatusInternalServerError)
			}
		}()
		c.Next()
	}
}
